/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact z_huatai@qq.com
 *  
 */
package org.zfes.snowy.generate.genarator;

import static org.mybatis.generator.internal.util.ClassloaderUtility.getCustomClassloader;
import static org.mybatis.generator.internal.util.StringUtility.composeFullyQualifiedTableName;
import static org.mybatis.generator.internal.util.messages.Messages.getString;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.GeneratedXmlFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.JavaTypeResolver;
import org.mybatis.generator.api.ProgressCallback;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.JavaClientGeneratorConfiguration;
import org.mybatis.generator.config.JavaModelGeneratorConfiguration;
import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.internal.ObjectFactory;
import org.mybatis.generator.internal.JDBCConnectionFactory;
import org.mybatis.generator.internal.NullProgressCallback;

import org.mybatis.generator.internal.db.DatabaseIntrospector;
import org.zfes.snowy.core.util.ZStrUtil;
import org.zfes.snowy.generate.implaments.MybatisMapperGeneratorImpl;
import org.zfes.snowy.generate.implaments.MybatisModelGeneratorImpl;
import org.zfes.snowy.generate.implaments.MybatisXmlGeneratorImpl;
import org.zfes.snowy.generate.interfaces.MybatisMapperGenerator;
import org.zfes.snowy.generate.interfaces.MybatisModelGenerator;
import org.zfes.snowy.generate.interfaces.MybatisXmlGenerator;
import org.zfes.snowy.generate.model.MybatisEntityModel;
import org.zfes.snowy.generate.model.MybatisMapperModel;
import org.zfes.snowy.generate.model.MybatisProperty;
import org.zfes.snowy.generate.model.MybatisXmlModel;
import org.zfes.snowy.generate.model.MybatisXmlProperty;

import com.google.common.collect.Lists;

public class MybatisGenerator {
	
    private Configuration configuration;
    private List<GeneratedJavaFile> generatedJavaFiles;
    private List<GeneratedXmlFile> generatedXmlFiles;
    private List<String> warnings;
    private List<IntrospectedTable> introspectedTables;
    public MybatisGenerator(Configuration configuration,List<String> warnings) throws InvalidConfigurationException {
        super();
        if (configuration == null) {
            throw new IllegalArgumentException(getString("RuntimeError.2")); //$NON-NLS-1$
        } else {
            this.configuration = configuration;
        }


        if (warnings == null) {
            this.warnings = new ArrayList<String>();
        } else {
            this.warnings = warnings;
        }
        generatedJavaFiles = new ArrayList<GeneratedJavaFile>();
        generatedXmlFiles = new ArrayList<GeneratedXmlFile>();
        this.configuration.validate();
    }
  
    public void generate(Set<String> contextIds,Set<String> fullyQualifiedTableNames) throws SQLException,IOException, InterruptedException {
    	 ProgressCallback callback = new NullProgressCallback();
    	  generatedJavaFiles.clear();
          generatedXmlFiles.clear();
          List<Context> contextsToRun;
          if (contextIds == null || contextIds.size() == 0) {
              contextsToRun = configuration.getContexts();
          } else {
              contextsToRun = new ArrayList<Context>();
              for (Context context : configuration.getContexts()) {
                  if (contextIds.contains(context.getId())) {
                      contextsToRun.add(context);
                  }
              }
          }
          if (configuration.getClassPathEntries().size() > 0) {
              ClassLoader classLoader = getCustomClassloader(configuration.getClassPathEntries());
              ObjectFactory.addExternalClassLoader(classLoader);
          }
          
          
          int totalSteps = 0;
          for (Context context : contextsToRun) {
              totalSteps += context.getIntrospectionSteps();
          }
          callback.introspectionStarted(totalSteps);

       
          //多个context
          for (Context context : contextsToRun) {
        	 String isRun= context.getProperty("isRun");
        	  if("false".equals(isRun)){
        		  continue;
        	  }
        	  if(introspectedTables!=null){
        		  introspectedTables.clear(); 
        	  }
        
        	  
        	  introspectTables(callback, context,warnings,fullyQualifiedTableNames);
        	  
        	for(IntrospectedTable introspectedTable: introspectedTables){
        System.out.println("---------------读到表： "+introspectedTable.getFullyQualifiedTable().getIntrospectedTableName()+"--domain name： "+introspectedTable.getFullyQualifiedTable().getDomainObjectName()+"---------------");
           	  FullyQualifiedTable table=introspectedTable.getFullyQualifiedTable();
           	  List<IntrospectedColumn> pk= introspectedTable.getPrimaryKeyColumns();
              List<IntrospectedColumn> bs= introspectedTable.getBaseColumns();
           	  List<IntrospectedColumn> bl=introspectedTable.getBLOBColumns();
          	List<IntrospectedColumn> totleColumnList=Lists.newArrayList();
         	totleColumnList.addAll(pk);
         	totleColumnList.addAll(bs);
         	totleColumnList.addAll(bl);
         	
          	SqlMapGeneratorConfiguration sqlMapGenConfig=context.getSqlMapGeneratorConfiguration();
        	JavaModelGeneratorConfiguration javaModelGenConfig=context.getJavaModelGeneratorConfiguration();
         	JavaClientGeneratorConfiguration javaClientGenConfig=	context.getJavaClientGeneratorConfiguration();
    	 	//start pro
         	String domianName=table.getDomainObjectName();
     	 	String mapper_xml_package=javaClientGenConfig.getTargetPackage();
     		String mapper_package=sqlMapGenConfig.getTargetPackage();
     		String model_package=javaModelGenConfig.getTargetPackage();
     		String tableName=table.getIntrospectedTableName();
     		
     		MybatisMapperModel mapperModel=new MybatisMapperModel();
     		mapperModel.setMapperFullClassName(mapper_package+"."+domianName+"Mapper");
    		mapperModel.setMapperSimpleClassName(domianName+"Mapper");
    		mapperModel.setMapperPackageName(mapper_package);
    		
    		mapperModel.setModelFullClassName(model_package+"."+domianName);
    		mapperModel.setModelSimpleClassName(domianName);
    		mapperModel.setModelPackageName(model_package);
    		
    		MybatisEntityModel entiyModel=new MybatisEntityModel();
    		entiyModel.setEntityFullClassName(model_package+"."+domianName);
    		entiyModel.setEntitySimpleClassName(domianName);
    		entiyModel.setPackageName(model_package);
    		List<MybatisProperty> propertyModelList=Lists.newArrayList();//
    		
    		MybatisXmlModel xmlModel=new MybatisXmlModel();
    		xmlModel.setMapperFullClassName(mapper_xml_package+"."+domianName+"Mapper");
    		xmlModel.setMapperSimpleClassName(domianName+"Mapper");
    		xmlModel.setMapperPackageName(mapper_xml_package);
    		
    		xmlModel.setModelFullClassName(model_package+"."+domianName);
    		xmlModel.setTableName(tableName);
    		
    		
    		List<MybatisXmlProperty> xmlPropertyList=Lists.newArrayList();//
         	for(IntrospectedColumn col:totleColumnList){
         		FullyQualifiedJavaType fulljavaType=col.getFullyQualifiedJavaType();
         		col.isNullable();
         		col.getLength();
         		col.getScale();
         		col.getRemarks();
         		System.out.println("---字段名： "+col.getActualColumnName()+"|| 备注： "+col.getRemarks()+"|| 可空： "+col.isNullable() +"||类型"+col.getJdbcTypeName()+"||长度： "+col.getLength()+"||Scale: "+col.getScale());
         		String columnName= col.getActualColumnName();
         	 	String jdbcType=col.getJdbcTypeName();
         	 	
         	 	String javaPropertyName=col.getJavaProperty();
         	 	String propertyType=fulljavaType.getFullyQualifiedName();
    			
            	final String uniqueReg="_U";
            	final String queryRegStart="_Q@";
            	final String queryRegEnd="@";
            	
            	final String dicRegStart="_D#";
            	final String dicRegEnd="#";
         	 	String remark=ZStrUtil.trimToEmpty(col.getRemarks());
         	 	
         	 	boolean unique=false;
         	 	if(remark.contains("_U")){
         	 		unique=true;
         	 		remark=remark.replace(uniqueReg, "");
         	 	}
         	 	if(remark.contains(dicRegStart)){
         	 		String xyb=ZStrUtil.substringAfter(remark, dicRegStart);
         	 		 xyb=ZStrUtil.substringBefore(xyb, dicRegEnd);
         	 		remark=remark.replace(dicRegStart+xyb+dicRegEnd, ""); 
         	 	}
         	 	boolean isQueryField=false;
         	 	String queryLogic="";
         	 	if(remark.contains(queryRegStart)){
         	 		 queryLogic=ZStrUtil.substringAfter(remark, queryRegStart);
         	 		queryLogic=ZStrUtil.substringBefore(queryLogic, queryRegEnd);
           		    remark=remark.replace(queryRegStart+queryLogic+queryRegEnd, ""); 
           		    isQueryField=true;
         	 	}
         		
         	 	if(remark.contains("D#")){
         	 		remark=ZStrUtil.substringBefore(remark, "##");
         	 	}
         	 	
         	 	MybatisProperty mybatisPropertyModel=
					         	 	new MybatisProperty( 
					         	 			javaPropertyName,
					         	 			propertyType,
					         	 			remark,
					         	 			col.isNullable(),
					         	 			col.getLength(), 
					         	 			col.getScale());
         	 	mybatisPropertyModel.setUnique(unique);
         	 	mybatisPropertyModel.setIsQueryField(isQueryField);
         	 	mybatisPropertyModel.setQueryLogic(queryLogic);
         	 	
         	 	propertyModelList.add(mybatisPropertyModel);
         	 	
         	 	MybatisXmlProperty xmlProperty=new MybatisXmlProperty(columnName, javaPropertyName,jdbcType, propertyType);
         	 	xmlProperty.setUnique(unique);
         	 	xmlProperty.setIsQueryField(isQueryField);
         	 	xmlProperty.setQueryLogic(queryLogic);
         	 	xmlPropertyList.add(xmlProperty);
        		
         	}
         	
         	entiyModel.setPropertyModelList(propertyModelList);
         	
         	xmlModel.setModelSimpleClassName(domianName);
         	xmlModel.setModelRalList(xmlPropertyList);
    	
         	mapperModel.setPropertyModelList(propertyModelList);
         	
    		MybatisMapperGenerator mybatisMapperGenerator=new MybatisMapperGeneratorImpl();
    		mybatisMapperGenerator.generateMapper(mapperModel);
    		
    		MybatisModelGenerator modelgen=new MybatisModelGeneratorImpl();
    		modelgen.generatorModel(entiyModel);
    		
    		MybatisXmlGenerator mybatisXmlGenerator=new MybatisXmlGeneratorImpl();
    		mybatisXmlGenerator.generatorXml(xmlModel);
    		
    		String controllerNameSpace=context.getProperty("nameSpace");
    		
    	  String isSimpleGen= context.getProperty("isSimpleGen");
    	  
       	  if("true".equals(isSimpleGen)){
       		String domainDesc= getDomainDesc( context, tableName);
       		
       		BizGenerator.simpleGen(domainDesc,controllerNameSpace,model_package, domianName,propertyModelList);
       	  }
    		
          }
       }
          
    }
  
    private String getDomainDesc(Context context,String tableName){
  	  List<TableConfiguration> tableConfigList=context.getTableConfigurations();
  	  if(tableConfigList!=null&&!tableConfigList.isEmpty()){
  		  for(TableConfiguration tableConfiguration:tableConfigList ){
  			  if((""+tableName).equals(tableConfiguration.getTableName())){
  				  return   tableConfiguration.getProperty("desc");
  			  }
  		  }
  	  }
  	return "";
    }

    public void introspectTables(ProgressCallback callback,Context context,
            List<String> warnings, Set<String> fullyQualifiedTableNames)
            throws SQLException, InterruptedException {
    	
    	introspectedTables = new ArrayList<IntrospectedTable>();
        JavaTypeResolver javaTypeResolver = ObjectFactory.createJavaTypeResolver(context, warnings);

        Connection connection = null;

        try {
            callback.startTask(getString("Progress.0")); //$NON-NLS-1$
            connection = getConnection(context);

            DatabaseIntrospector databaseIntrospector = new DatabaseIntrospector(context, connection.getMetaData(), javaTypeResolver, warnings);
            List<TableConfiguration>  tableConfigurations= context.getTableConfigurations();
            for (TableConfiguration tc : tableConfigurations) {
                String tableName = composeFullyQualifiedTableName(tc.getCatalog(), tc.getSchema(), tc.getTableName(), '.');

                if (fullyQualifiedTableNames != null && fullyQualifiedTableNames.size() > 0) {
                    if (!fullyQualifiedTableNames.contains(tableName)) {
                        continue;
                    }
                }

                if (!tc.areAnyStatementsEnabled()) {
                    warnings.add(getString("Warning.0", tableName)); //$NON-NLS-1$
                    continue;
                }

                callback.startTask(getString("Progress.1", tableName)); //$NON-NLS-1$
                List<IntrospectedTable> tables = databaseIntrospector.introspectTables(tc);

                if (tables != null) {
                    introspectedTables.addAll(tables);
                }

                callback.checkCancel();
            }
        } finally {
            closeConnection(connection);
        }
    }
    
    private Connection getConnection(Context context) throws SQLException {
       // Connection connection = ConnectionFactory.getInstance().getConnection(context.getJdbcConnectionConfiguration());
        Connection connection = new JDBCConnectionFactory(context.getJdbcConnectionConfiguration()).getConnection();//v1.3.5

        return connection;
    }

    private void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                // ignore
                ;
            }
        }
    }
}
